import { ELineType, IExportParamModelData, ILineData, Number2, Number3 } from "@manycore/custom-miniapp-sdk"
import { BufferGeometry, ColorRepresentation, EdgesGeometry, Euler, ExtrudeGeometry, Group, LineBasicMaterial, LineSegments, Mesh, MeshStandardMaterial, Object3D, Shape } from "three"
import { getFloorModels, xyz } from "./helper"
import { getArc2Angle, getArcCenter, getEllArc2Angle } from "./math"
import { ELineType2, ILineData2, IPathData2 } from "./type"

/** 填充形状数据 */
export function fillShapeData(model: IExportParamModelData): IExportParamModelData {
  const { size } = model;
  const center = { x: 0, y: 0, z: 0 }
  const resultPoints = [
    { x: center.x - size.x / 2, y: center.y - size.y / 2 },
    { x: center.x - size.x / 2, y: center.y + size.y / 2 },
    { x: center.x + size.x / 2, y: center.y + size.y / 2 },
    { x: center.x + size.x / 2, y: center.y - size.y / 2 },
  ];
  const thickness = size.z;
  const path = {
    resultPoints,
    resultLines: new Array(resultPoints.length).fill({})
  }
  const paramPlankPath: any = { path }
  return { ...model, thickness, paramPlankPath };
}
/** 获取形状数据 */
export function getShapeData(model: IExportParamModelData) {
  const { paramPlankPath: { path, holes } } = model
  return { path, holes } as unknown as { path: IPathData2, holes: IPathData2[] }
}
/** 形状还原 */
export function getShape(points: Number2[], lines: ILineData[]) {
  const shape = new Shape()
  const [startPoint, ...otherPoints] = points
  if (points.length === lines.length) otherPoints.push(points[0])
  shape.moveTo(startPoint.x, startPoint.y)
  otherPoints.forEach((point, index) => {
    const line = lines[index]
    const get2p = () => {
      const p1 = (points[index])
      const p2 = (point)
      return [p1, p2]
    }
    const getLine = () => line as ILineData2
    switch (line.type as ELineType | ELineType2) {
      case ELineType.SEGMENT:
        shape.lineTo(point.x, point.y)
        break;
      // 圆弧还原逻辑
      case ELineType.CIRCLE_ARC:
        {
          const [p1, p2] = get2p()
          const { minorArc, clockwise, radius } = getLine()
          const center = getArcCenter(p1, p2, radius, minorArc, !clockwise)
          const [startAngle, endAngle] = getArc2Angle(center, p1, p2)
          const { x: cx, y: cy } = center
          shape.absarc(cx, cy, radius, startAngle, endAngle, clockwise);
        }
        break;
      // 椭圆弧还原逻辑
      case ELineType2.ELLIPSE_ARC:
        {
          const [p1, p2] = get2p()
          const { clockwise, curve2d, curve2d: { a, b, c } } = getLine()
          const { x: cx, y: cy } = c
          const [startAngle, endAngle] = getEllArc2Angle(p1, p2, !clockwise, curve2d)
          shape.absellipse(cx, cy, a, b, startAngle, endAngle, clockwise, 0)
        }
        break;

      default: shape.lineTo(point.x, point.y)
        break;
    }
  })
  return shape
}
/** 根据模型数据，还原形状 */
export function getShapeFromModel(model: IExportParamModelData) {
  const { path, holes } = getShapeData(model);
  const plankShape = getShape(path.resultPoints, path.resultLines);
  if (holes) {
    plankShape.holes = holes.map(hole => getShape(hole.resultPoints, hole.resultLines));
  }
  return plankShape;
}
/** 板件材质设置 */
export function getPlank(plankShape: Shape, thickness: number) {
  return new Mesh(
    new ExtrudeGeometry(plankShape, { depth: thickness }),
    new MeshStandardMaterial({ color: 'orange' })
  );
}
/** 描边 */
export function getEdge(geometry: BufferGeometry, color: ColorRepresentation | undefined) {
  const geo = new EdgesGeometry(geometry);
  const mat = new LineBasicMaterial({ color, linewidth: 2 });
  const edge = new LineSegments(geo, mat);
  return edge
}
/** 校准模型初始位置 */
export function getCalibratedGroup(offset: Number3, ...obj3d: Object3D[]) {
  const offsetGroup = new Group()
  offsetGroup.add(...obj3d)
  offsetGroup.children.forEach(e => e.position.set(...xyz(offset)))
  return offsetGroup
}
/** 获取模型初始位置的校准偏移值 */
export function getCalibratedOffset(thickness: number) {
  return { x: 0, y: 0, z: -thickness / 2 }
}
export function paramModelRegeneration(model: IExportParamModelData) {
  const { modelTypeId } = model
  if (modelTypeId === 2) model = fillShapeData(model)
  const { rotate, position, thickness } = model;
  if (modelTypeId === 3 || modelTypeId === 2) {
    const plankShape = getShapeFromModel(model)
    // 板件材质设置
    const plank = getPlank(plankShape, thickness)
    // 板件描边
    const plankEdge = getEdge(plank.geometry, 'black')
    // 板件模型初始位置校准
    const offset = getCalibratedOffset(thickness)
    const calibratedGroup = getCalibratedGroup(offset, plank, plankEdge)
    calibratedGroup.setRotationFromEuler(new Euler(...xyz(rotate!), 'ZYX'))
    // 板件位置还原
    calibratedGroup.position.set(...xyz(position!))
    return calibratedGroup
  }
}
/** 使用绝对位置，还原元件形状与位置 */
export function addShapeAbsolute(model: IExportParamModelData) {
  const floorModel = getFloorModels(model)
  const shapes = floorModel.map(model => {
    const { absPosition: position, absRotation: rotate } = model
    return paramModelRegeneration({ ...model, position, rotate })!
  }
  )
  return new Group().add(...shapes)
}
/** 使用相对位置，还原元件形状与位置 */
export function addShapeRelative(model: IExportParamModelData) {
  const { scale, rotate, position } = model;
  const group = new Group()
  group.position.set(...xyz(position!))
  group.rotation.set(...xyz(rotate!))
  group.scale.set(...xyz(scale))
  const shapes = model.subModels.map(model =>
    paramModelRegeneration(model) || addShapeRelative(model)
  )
  group.add(...shapes)
  return group
}